package com.holly.unit.genx;

import cn.hutool.core.util.StrUtil;
import com.holly.unit.gen.api.config.GenCodeCfg;
import com.holly.unit.gen.api.config.GenFileCfg;
import com.holly.unit.gen.api.constants.GenFileCfgConstants;
import com.holly.unit.gen.api.util.FileUtil;
import com.holly.unit.gen.api.util.FreemarkerUtil;
import freemarker.template.Configuration;
import freemarker.template.Template;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 类描述: 代码生成器抽象类
 *
 * @author fxr
 * @version 1.0.0
 * @date 2022/1/14 15:52
 */
public abstract class AbstractGenerateCode {

    protected List<String> genFileRes = new ArrayList<>();


    protected void genFileAbsolutePath(GenFileCfg cfg, String projectPath, Map<String,Object> tempData) throws Exception{

        for (int i=0;i<cfg.getTmpRootDirs().size();i++) {
            File tmpRootDir = cfg.getTmpRootDirs().get(i);
            process(projectPath,tmpRootDir,tempData,cfg);
        }

    }

    protected void process(String projectPath, File tmpRootDir, Map<String, Object> tempData, GenFileCfg cfg) throws Exception{

        if (tmpRootDir == null) throw new IllegalStateException("template root dir is not null");
        //循环子目录
        List<File> subFiles = FileUtil.queryAllNoIgnoreFile(tmpRootDir);
        for (int i=0;i<subFiles.size();i++) {
            File subFile = subFiles.get(i);
            execute(projectPath,tmpRootDir,tempData,subFile,cfg);
        }
    }


    protected void execute(String projectPath, File tmpRootDir, Map<String, Object> tempData, File subFile, GenFileCfg cfg) throws Exception{
        String templateFile = FileUtil.getRelativePath(tmpRootDir,subFile);
        if (StrUtil.isNotBlank(cfg.getCustomePath()) && templateFile(templateFile,cfg)) return ;
        String outPutFilePath = handleOutPutFilePath(tempData,templateFile,cfg);
        if (outPutFilePath.startsWith("java")) {
            String path = projectPath+File.separator+ GenCodeCfg.getSrcPackage();
            String source = path;
            outPutFilePath = outPutFilePath.substring("java".length());
            outPutFilePath = source+outPutFilePath;
            // 生成文件
            generateFileOrWriterFile(templateFile,outPutFilePath,tempData,cfg);
        } else if (outPutFilePath.startsWith("webapp")) {
            String path = projectPath+File.separator+GenCodeCfg.getWebPackage();
            String source = path;
            outPutFilePath = outPutFilePath.substring("webapp".length());
            outPutFilePath = source+outPutFilePath;
            generateFileOrWriterFile(templateFile,outPutFilePath,tempData,cfg);
        }
    }

    protected void generateFileOrWriterFile(String templateFile, String outPutFilePath, Map<String, Object> tempData, GenFileCfg cfg) throws Exception {

        if (outPutFilePath.endsWith("i")) {
            outPutFilePath = outPutFilePath.substring(0,outPutFilePath.length()-1);
        }
        Template template = getFreemarkerTemplate(templateFile,cfg);
        template.setOutputEncoding(GenFileCfgConstants.SRC_ENCODE);
        File absoluteOutputFilePath = FileUtil.createParentDir(outPutFilePath);
        FreemarkerUtil.generatorTemplate(template,tempData,absoluteOutputFilePath,GenFileCfgConstants.SRC_ENCODE);
        if (!isCutFile(absoluteOutputFilePath)) {
            genFileRes.add("生成成功:"+outPutFilePath);
        }

        if (isCutFile(absoluteOutputFilePath)) {
            //分割文件
            cutFile(absoluteOutputFilePath,"#segment#");
        }
    }

    protected void cutFile(File file,String split) {
        InputStreamReader isr = null;
        BufferedReader br = null;
        List<Writer> flist = new ArrayList<>();

        try{
            isr = new InputStreamReader(new FileInputStream(file),GenFileCfgConstants.SRC_ENCODE);
            br = new BufferedReader(isr);
            String row;
            boolean start = false;//开始分割标志
            Writer tarFile = null;
            while ((row=br.readLine()) != null) {
                if (row.trim().length()>0 && row.startsWith(split)) {
                    String fileName = row.substring(split.length());
                    String parent = file.getParentFile().getAbsolutePath();
                    fileName = parent+File.separator+fileName;
                    tarFile = new OutputStreamWriter(new FileOutputStream(fileName),GenFileCfgConstants.SRC_ENCODE);
                    flist.add(tarFile);
                    genFileRes.add("生成成功:"+fileName);
                    start = true;
                } else if (start) {
                    tarFile.append(row+"\r\n");
                }
            }

            for (int i=0;i<flist.size();i++) {
                flist.get(i).close();
            }
            br.close();
            isr.close();

            forceDel(file);

        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            try{
                if (br != null) {
                    br.close();
                }
                if (isr != null) {
                    isr.close();
                }
                if (flist.size() > 0) {
                    for (int i=0;i<flist.size();i++) {
                        if (flist.get(i) != null) {
                            flist.get(i).close();
                        }
                    }
                }
            }catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    protected static boolean  forceDel(File file) {

        boolean res = false;

        int tryCount = 0;

        while (!res && tryCount++ <10){
            System.gc();
            res = file.delete();
        }

        return res;

    }

    /***
     * 判断是否分割模板
     * @param file
     * @return
     */
    protected boolean isCutFile(File file) {
        if (file.getName().startsWith("[1-n]")) {
            return true;
        }
        return false;
    }

    protected Template getFreemarkerTemplate(String templateFile, GenFileCfg cfg) throws IOException{

        return FreemarkerUtil.initConf(cfg.getTmpRootDirs(),GenFileCfgConstants.SRC_ENCODE).getTemplate(templateFile);

    }

    protected String handleOutPutFilePath(Map<String, Object> tempData, String templateFile, GenFileCfg cfg) throws Exception{

        String outputFilePath = templateFile;
        int testExpressionIndex = -1;
        if ((testExpressionIndex = templateFile.indexOf('@')) !=-1){
            outputFilePath = templateFile.substring(0,testExpressionIndex);
            String testExpressionKey = templateFile.substring(testExpressionIndex+1);
            Object expressionVal = tempData.get(testExpressionKey);
            if (expressionVal == null) {
                return null;
            }

            if (!"true".equals(String.valueOf(expressionVal))) {
                return null;
            }
        }
        Configuration conf = FreemarkerUtil.initConf(cfg.getTmpRootDirs(),GenFileCfgConstants.SRC_ENCODE);
        outputFilePath = FreemarkerUtil.generatorTmplateStr(outputFilePath,tempData,conf);
        String customerPath = cfg.getCustomePath();
        if (StrUtil.isNotBlank(customerPath)) {
            outputFilePath = outputFilePath.substring(customerPath.length()+1);
        }
        String extName = outputFilePath.substring(outputFilePath.lastIndexOf("."));
        String fileName = outputFilePath.substring(0,outputFilePath.lastIndexOf(".")).replace(".",File.separator);
        outputFilePath = fileName+extName;

        return outputFilePath;
    }


    private boolean templateFile(String templateFile,GenFileCfg cfg) {
        return !templateFile.replace(File.separator,".").startsWith(cfg.getCustomePath());
    }


    /***
     * 去除头尾指定字符串
     * @param str
     * @param element
     * @return
     */
    protected static String trimHeadAndTailChar(String str,String element) {
        boolean headIndexFlag = true;
        boolean tailIndexFlag = true;
        do{
            int headIndex = str.indexOf(element) == 0 ? 1:0;
            int tailIndex = str.lastIndexOf(element)+1 == str.length() ? str.lastIndexOf(element):str.length();
            str = str.substring(headIndex,tailIndex);
            headIndexFlag = str.indexOf(element) ==0;
            tailIndexFlag = (str.lastIndexOf(element)+1 == str.length());
        }while (headIndexFlag || tailIndexFlag);
        return str;
    }






}
